<template>
  <div class="base-form">
    <el-form
      ref="formRef"
      :model="formData"
      :rules="rules"
      :inline="inline"
      :label-position="labelPosition"
      :label-width="labelWidth"
      :size="size"
      :disabled="disabled"
      :validate-on-rule-change="validateOnRuleChange"
      :scroll-to-error="scrollToError"
      class="base-form-container"
    >
      <!-- 动态表单项 -->
      <template v-for="item in formItems" :key="item.prop">
        <el-form-item
          v-if="!item.hidden"
          :label="item.label"
          :prop="item.prop"
          :rules="item.rules"
          :required="item.required"
          :label-width="item.labelWidth"
        >
          <!-- 输入框 -->
          <el-input
            v-if="item.type === 'input'"
            v-model="formData[item.prop]"
            :type="item.inputType || 'text'"
            :placeholder="item.placeholder || `请输入${item.label}`"
            :disabled="item.disabled"
            :readonly="item.readonly"
            :clearable="item.clearable !== false"
            :maxlength="item.maxlength"
            :show-word-limit="item.showWordLimit"
            :prefix-icon="item.prefixIcon"
            :suffix-icon="item.suffixIcon"
            :rows="item.rows"
            :autosize="item.autosize"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
            @blur="handleFormItemBlur(item.prop, formData[item.prop])"
          />

          <!-- 文本域 -->
          <el-input
            v-else-if="item.type === 'textarea'"
            v-model="formData[item.prop]"
            type="textarea"
            :placeholder="item.placeholder || `请输入${item.label}`"
            :disabled="item.disabled"
            :readonly="item.readonly"
            :clearable="item.clearable !== false"
            :maxlength="item.maxlength"
            :show-word-limit="item.showWordLimit"
            :rows="item.rows || 3"
            :autosize="item.autosize"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 数字输入框 -->
          <el-input-number
            v-else-if="item.type === 'number'"
            v-model="formData[item.prop]"
            :placeholder="item.placeholder || `请输入${item.label}`"
            :disabled="item.disabled"
            :min="item.min"
            :max="item.max"
            :step="item.step || 1"
            :precision="item.precision"
            :controls="item.controls !== false"
            :controls-position="item.controlsPosition"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 下拉选择框 -->
          <el-select
            v-else-if="item.type === 'select'"
            v-model="formData[item.prop]"
            :placeholder="item.placeholder || `请选择${item.label}`"
            :disabled="item.disabled"
            :clearable="item.clearable !== false"
            :multiple="item.multiple"
            :collapse-tags="item.collapseTags"
            :collapse-tags-tooltip="item.collapseTagsTooltip"
            :filterable="item.filterable"
            :allow-create="item.allowCreate"
            :remote="item.remote"
            :remote-method="item.remoteMethod"
            :loading="item.loading"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          >
            <el-option
              v-for="option in item.options"
              :key="option.value"
              :label="option.label"
              :value="option.value"
              :disabled="option.disabled"
            />
          </el-select>

          <!-- 级联选择器 -->
          <el-cascader
            v-else-if="item.type === 'cascader'"
            v-model="formData[item.prop]"
            :options="item.options"
            :placeholder="item.placeholder || `请选择${item.label}`"
            :disabled="item.disabled"
            :clearable="item.clearable !== false"
            :show-all-levels="item.showAllLevels !== false"
            :props="item.cascaderProps"
            :filterable="item.filterable"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 日期选择器 -->
          <el-date-picker
            v-else-if="item.type === 'date'"
            v-model="formData[item.prop]"
            :type="item.dateType || 'date'"
            :placeholder="item.placeholder || `请选择${item.label}`"
            :disabled="item.disabled"
            :clearable="item.clearable !== false"
            :format="item.format"
            :value-format="item.valueFormat"
            :start-placeholder="item.startPlaceholder || '开始日期'"
            :end-placeholder="item.endPlaceholder || '结束日期'"
            :range-separator="item.rangeSeparator || '至'"
            :shortcuts="item.shortcuts"
            :disabled-date="item.disabledDate"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 时间选择器 -->
          <el-time-picker
            v-else-if="item.type === 'time'"
            v-model="formData[item.prop]"
            :placeholder="item.placeholder || `请选择${item.label}`"
            :disabled="item.disabled"
            :clearable="item.clearable !== false"
            :format="item.format"
            :value-format="item.valueFormat"
            :is-range="item.isRange"
            :range-separator="item.rangeSeparator || '至'"
            :start-placeholder="item.startPlaceholder || '开始时间'"
            :end-placeholder="item.endPlaceholder || '结束时间'"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 开关 -->
          <el-switch
            v-else-if="item.type === 'switch'"
            v-model="formData[item.prop]"
            :disabled="item.disabled"
            :active-text="item.activeText"
            :inactive-text="item.inactiveText"
            :active-value="item.activeValue ?? true"
            :inactive-value="item.inactiveValue ?? false"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 单选框组 -->
          <el-radio-group
            v-else-if="item.type === 'radio'"
            v-model="formData[item.prop]"
            :disabled="item.disabled"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          >
            <component
              :is="item.radioType === 'button' ? 'el-radio-button' : 'el-radio'"
              v-for="option in item.options"
              :key="option.value"
              :label="option.value"
              :disabled="option.disabled"
            >
              {{ option.label }}
            </component>
          </el-radio-group>

          <!-- 复选框组 -->
          <el-checkbox-group
            v-else-if="item.type === 'checkbox'"
            v-model="formData[item.prop]"
            :disabled="item.disabled"
            :min="item.min"
            :max="item.max"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          >
            <component
              :is="item.checkboxType === 'button' ? 'el-checkbox-button' : 'el-checkbox'"
              v-for="option in item.options"
              :key="option.value"
              :label="option.value"
              :disabled="option.disabled"
            >
              {{ option.label }}
            </component>
          </el-checkbox-group>

          <!-- 滑块 -->
          <el-slider
            v-else-if="item.type === 'slider'"
            v-model="formData[item.prop]"
            :disabled="item.disabled"
            :min="item.min || 0"
            :max="item.max || 100"
            :step="item.step || 1"
            :show-stops="item.showStops"
            :show-tooltip="item.showTooltip !== false"
            :range="item.range"
            :marks="item.marks"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 评分 -->
          <el-rate
            v-else-if="item.type === 'rate'"
            v-model="formData[item.prop]"
            :disabled="item.disabled"
            :max="item.max || 5"
            :allow-half="item.allowHalf"
            :show-text="item.showText"
            :show-score="item.showScore"
            :texts="item.texts"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 颜色选择器 -->
          <el-color-picker
            v-else-if="item.type === 'color'"
            v-model="formData[item.prop]"
            :disabled="item.disabled"
            :show-alpha="item.showAlpha"
            :color-format="item.colorFormat"
            @change="handleFormItemChange(item.prop, formData[item.prop])"
          />

          <!-- 上传 -->
          <el-upload
            v-else-if="item.type === 'upload'"
            :action="item.action || '#'"
            :multiple="item.multiple"
            :limit="item.limit"
            :file-list="formData[item.prop] || []"
            :accept="item.accept"
            :list-type="item.listType || 'text'"
            :disabled="item.disabled"
            :http-request="item.httpRequest"
            :before-upload="item.beforeUpload"
            :on-success="
              (response: any, file: any, fileList: any) =>
                handleUploadSuccess(item.prop, response, file, fileList)
            "
            :on-remove="(file: any, fileList: any) => handleUploadRemove(item.prop, file, fileList)"
            :on-exceed="item.onExceed"
          >
            <template v-if="item.listType === 'picture-card'">
              <el-icon><Plus /></el-icon>
            </template>
            <template v-else>
              <el-button type="primary">
                <el-icon><Upload /></el-icon>
                {{ item.uploadText || '点击上传' }}
              </el-button>
            </template>
          </el-upload>

          <!-- 自定义插槽 -->
          <slot
            v-else-if="item.type === 'slot'"
            :name="item.slotName || item.prop"
            :form-data="formData"
            :item="item"
          ></slot>

          <!-- 提示文本 -->
          <div v-if="item.tip" class="form-item-tip">{{ item.tip }}</div>
        </el-form-item>
      </template>

      <!-- 自定义插槽 -->
      <slot name="extra" :form-data="formData"></slot>

      <!-- 操作按钮 -->
      <el-form-item v-if="showButtons">
        <div class="form-buttons">
          <el-button
            type="primary"
            :loading="submitLoading"
            :disabled="disabled"
            @click="handleSubmit"
          >
            {{ submitButtonText }}
          </el-button>
          <el-button @click="handleReset" :disabled="disabled">
            {{ resetButtonText }}
          </el-button>
          <slot name="buttons" :form-data="formData"></slot>
        </div>
      </el-form-item>
    </el-form>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, watch, onMounted, nextTick } from 'vue';
import { Plus, Upload } from '@element-plus/icons-vue';
import type { FormInstance, FormRules } from 'element-plus';

interface FormItemOption {
  label: string;
  value: any;
  disabled?: boolean;
  children?: FormItemOption[];
  [key: string]: any;
}

export interface BaseFormItem {
  type:
    | 'input'
    | 'textarea'
    | 'number'
    | 'select'
    | 'cascader'
    | 'date'
    | 'time'
    | 'switch'
    | 'radio'
    | 'checkbox'
    | 'slider'
    | 'rate'
    | 'color'
    | 'upload'
    | 'slot';
  label: string;
  prop: string;
  placeholder?: string;
  disabled?: boolean;
  readonly?: boolean;
  clearable?: boolean;
  hidden?: boolean;
  required?: boolean;
  rules?: any[];
  tip?: string;
  labelWidth?: string | number;
  options?: FormItemOption[];
  // input/textarea
  inputType?: 'text' | 'password' | 'number' | 'email' | 'tel' | 'url';
  maxlength?: number;
  showWordLimit?: boolean;
  prefixIcon?: any;
  suffixIcon?: any;
  rows?: number;
  autosize?: boolean | { minRows?: number; maxRows?: number };
  // number
  min?: number;
  max?: number;
  step?: number;
  precision?: number;
  controls?: boolean;
  controlsPosition?: 'right' | '';
  // select
  multiple?: boolean;
  collapseTags?: boolean;
  collapseTagsTooltip?: boolean;
  filterable?: boolean;
  allowCreate?: boolean;
  remote?: boolean;
  remoteMethod?: (query: string) => void;
  loading?: boolean;
  // cascader
  showAllLevels?: boolean;
  cascaderProps?: any;
  // date
  dateType?:
    | 'date'
    | 'week'
    | 'month'
    | 'year'
    | 'datetime'
    | 'datetimerange'
    | 'daterange'
    | 'monthrange';
  format?: string;
  valueFormat?: string;
  startPlaceholder?: string;
  endPlaceholder?: string;
  rangeSeparator?: string;
  shortcuts?: any[];
  disabledDate?: (date: Date) => boolean;
  // time
  isRange?: boolean;
  // switch
  activeText?: string;
  inactiveText?: string;
  activeValue?: any;
  inactiveValue?: any;
  // radio/checkbox
  radioType?: 'default' | 'button';
  checkboxType?: 'default' | 'button';
  // slider
  range?: boolean;
  showStops?: boolean;
  showTooltip?: boolean;
  marks?: Record<number, any>;
  // rate
  allowHalf?: boolean;
  showText?: boolean;
  showScore?: boolean;
  texts?: string[];
  // color
  showAlpha?: boolean;
  colorFormat?: 'hsl' | 'hsv' | 'hex' | 'rgb';
  // upload
  action?: string;
  limit?: number;
  accept?: string;
  listType?: 'text' | 'picture' | 'picture-card';
  httpRequest?: (options: any) => any;
  beforeUpload?: (file: any) => boolean | Promise<boolean>;
  onExceed?: (files: any[], fileList: any[]) => void;
  uploadText?: string;
  // slot
  slotName?: string;
}

interface Props {
  formItems: BaseFormItem[];
  modelValue?: Record<string, any>;
  rules?: FormRules;
  inline?: boolean;
  labelPosition?: 'top' | 'left' | 'right';
  labelWidth?: string | number;
  size?: 'large' | 'default' | 'small';
  disabled?: boolean;
  validateOnRuleChange?: boolean;
  scrollToError?: boolean;
  showButtons?: boolean;
  submitButtonText?: string;
  resetButtonText?: string;
  submitLoading?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  formItems: () => [],
  modelValue: () => ({}),
  inline: false,
  labelPosition: 'right',
  labelWidth: 'auto',
  size: 'default',
  disabled: false,
  validateOnRuleChange: true,
  scrollToError: true,
  showButtons: true,
  submitButtonText: '提交',
  resetButtonText: '重置',
  submitLoading: false
});

const emit = defineEmits<{
  (e: 'update:modelValue', formData: Record<string, any>): void;
  (e: 'submit', formData: Record<string, any>): void;
  (e: 'reset'): void;
  (e: 'change', prop: string, value: any): void;
  (e: 'blur', prop: string, value: any): void;
}>();

// 表单引用
const formRef = ref<FormInstance>();

// 表单数据
const formData = reactive<Record<string, any>>({});

// 保存初始值的快照（用于重置）
const initialFormData = ref<Record<string, any>>({});

// 标志位，避免循环更新
const isInternalUpdate = ref(false);

// 获取表单项的默认值
const getDefaultValue = (item: BaseFormItem) => {
  switch (item.type) {
    case 'checkbox':
      return [];
    case 'switch':
      return item.inactiveValue ?? false;
    case 'number':
      return item.min ?? 0;
    case 'upload':
      return [];
    default:
      return '';
  }
};

// 初始化表单数据
const initFormData = (saveSnapshot = false) => {
  // 清空表单数据
  Object.keys(formData).forEach(key => {
    delete formData[key];
  });

  // 根据formItems初始化表单数据
  props.formItems.forEach(item => {
    const initialValue = props.modelValue[item.prop];

    // 设置初始值
    if (initialValue !== undefined) {
      formData[item.prop] = initialValue;
    } else {
      formData[item.prop] = getDefaultValue(item);
    }
  });

  // 保存初始值快照（深拷贝）
  if (saveSnapshot) {
    initialFormData.value = JSON.parse(JSON.stringify(formData));
  }
};

// 监听 modelValue 变化
watch(
  () => props.modelValue,
  () => {
    // 如果是内部更新触发的，忽略
    if (isInternalUpdate.value) {
      return;
    }
    // 外部数据变化时，重新初始化并保存新的快照
    initFormData(true);
  },
  { deep: true }
);

// 处理表单项变化
const handleFormItemChange = (prop: string, value: any) => {
  emit('change', prop, value);
  // 只在表单项变化时同步到 modelValue
  isInternalUpdate.value = true;
  emit('update:modelValue', { ...formData });
  // 在下一个 tick 后重置标志位
  nextTick(() => {
    isInternalUpdate.value = false;
  });
};

// 处理表单项失焦
const handleFormItemBlur = (prop: string, value: any) => {
  emit('blur', prop, value);
};

// 处理上传成功
const handleUploadSuccess = (prop: string, _response: any, _file: any, fileList: any[]) => {
  formData[prop] = fileList;
  handleFormItemChange(prop, fileList);
};

// 处理删除文件
const handleUploadRemove = (prop: string, _file: any, fileList: any[]) => {
  formData[prop] = fileList;
  handleFormItemChange(prop, fileList);
};

// 处理提交
const handleSubmit = () => {
  formRef.value?.validate(valid => {
    if (valid) {
      emit('submit', { ...formData });
    }
  });
};

// 处理重置
const handleReset = () => {
  // 清除验证
  formRef.value?.resetFields();

  // 恢复到初始快照（深拷贝）
  const resetData = JSON.parse(JSON.stringify(initialFormData.value));
  Object.keys(formData).forEach(key => {
    delete formData[key];
  });
  Object.assign(formData, resetData);

  // 同步到父组件
  isInternalUpdate.value = true;
  emit('update:modelValue', { ...formData });
  nextTick(() => {
    isInternalUpdate.value = false;
  });

  emit('reset');
};

// 暴露方法供父组件调用
const validate = () => {
  return formRef.value?.validate();
};

const validateField = (props: string | string[]) => {
  return formRef.value?.validateField(props);
};

const resetFields = () => {
  // 清除验证
  formRef.value?.resetFields();

  // 恢复到初始快照（深拷贝）
  const resetData = JSON.parse(JSON.stringify(initialFormData.value));
  Object.keys(formData).forEach(key => {
    delete formData[key];
  });
  Object.assign(formData, resetData);

  // 同步到父组件
  isInternalUpdate.value = true;
  emit('update:modelValue', { ...formData });
  nextTick(() => {
    isInternalUpdate.value = false;
  });
};

const clearValidate = (props?: string | string[]) => {
  formRef.value?.clearValidate(props);
};

const scrollToField = (prop: string) => {
  formRef.value?.scrollToField(prop);
};

defineExpose({
  validate,
  validateField,
  resetFields,
  clearValidate,
  scrollToField,
  formData
});

// 组件挂载时初始化表单数据并保存快照
onMounted(() => {
  initFormData(true); // 保存初始值快照
});
</script>

<style lang="scss" scoped>
.base-form {
  width: 100%;

  .base-form-container {
    .form-item-tip {
      font-size: 12px;
      color: var(--el-text-color-secondary);
      line-height: 1.5;
      margin-top: 4px;
    }
  }

  .form-buttons {
    display: flex;
    gap: 10px;
    margin-top: 20px;
  }
}

// 主题适配
:deep(.el-form-item__label) {
  color: var(--theme-text-color);
}

:deep(.el-input__wrapper) {
  background-color: var(--theme-element-bg);
}

:deep(.el-input__inner) {
  color: var(--theme-text-color);
}

:deep(.el-textarea__inner) {
  background-color: var(--theme-element-bg);
  color: var(--theme-text-color);
}

:deep(.el-select__wrapper) {
  background-color: var(--theme-element-bg);
}
</style>
